package com.functest.orm
import groovy.transform.ToString


/**
 * @author DavydovMD
 * Date: 28.03.13
 *
 * Though, in theory, Mapper fields implements BuilderSupport,
 * we prefer not to use it, and use to adding fields by simply calling field() object
 */
@ToString()
class MapperFields extends BuilderSupport {
    List<Field> fields = []

    MapperFields copy() {
        MapperFields builderCopy = new MapperFields ()
        builderCopy.fields = []
        fields.each {
            builderCopy.fields << it.copy ()
        }
        builderCopy
    }

    MapperFields copy(String prefix) {
        MapperFields builderCopy = new MapperFields ()
        builderCopy.fields = []
        fields.each {
            builderCopy.fields << it.copy (prefix)
        }
        builderCopy
    }

    /**
     * Copy all fields from another MapperFields (retaining fields that already exists)
     * @param mb mapperBuilder to copy
     * @return this
     */

    MapperFields add(MapperFields mb) {
        mb.fields.each {Field inField ->
            if (!fields.grep { Field fld -> fld.name == inField.name }) {
                fields.add (inField.copy ())
            }
        }
        this
    }

    /**
     * Add fields using closure
     * @param closure
     * @return
     */

    MapperFields build(Closure closure) {
        this.with (closure)
        this
    }

    void field(String name, Object from = [name], Closure transform = null, Closure transformBack = null) {
        Map params = [:].with {
            put ('name', name)
            put ('from', from)
            if (transform != null) put ('transform', transform)
            if (transformBack != null) put ('transformBack', transformBack)
            delegate as Map
        }

        field (new Field (params))
    }

    void field(Map params) {
        fields << new Field (params)
    }

    void field(Field field) {
        fields << field
    }

    @Override
    protected void setParent(Object parent, Object child) {
    }

    private static Object parseName(Object name) {
        (name as String).replace ('$', '')
    }

    @Override
    protected Object createNode(Object name) {
        fields << new Field (from: [parseName (name)], name: parseName (name))
    }

    @Override
    protected Object createNode(Object name, Object value) {
        fields << new Field (from: (value instanceof List ? value : [value]), name: parseName (name))
    }

    @Override
    protected Object createNode(Object name, Map attributes) {
        def params = [name: parseName (name), *: attributes]
        fields << new Field (params)
    }

    @Override
    protected Object createNode(Object name, Map attributes, Object value) {
        if (value instanceof Closure) {
            def params = [transform: value as Closure, *: attributes]
            fields << createNode (parseName (name), params)
        } else {
            /* Ignore value parameter*/
            fields << createNode (parseName (name), attributes)
        }
    }

    /**
     * Get field by its fully qualified name
     *
     * Important: Appropriate support for fully qualified field names is not yet implemented.
     * Current implementation is no more than a workaround for fully qualified field names concept design flaws.
     * The actual problem lies in MultiMapper's usage of MapperFields
     * @param name
     * @return Field object
     * @throws NoSuchElementException
     */
    Field getField(String inName) {
        String name = Field.simplifyName (inName)
        try {
            if (Field.isSimpleName (name)) {
                return fields.grep {
                    Field f -> (f.simpleName) == name
                }.first ()
            }

            return fields.grep {
                Field f -> name == f.name
            }.first ()
        }

        catch (NoSuchElementException ignored) {
            throw new NoSuchFieldException ("No such field defined: ${name}")
        }
    }

    List<String> mappedFields() {
        List ret = []
        fields.each {
            (it.from instanceof Collection) ?
                ret.addAll (it.from as Collection)
            : ret << it.from
        }
        ret
    }

}
